﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Dapper;
using NiuX.Dapper.Abstractions;
using NiuX.Dapper.Repositories;
using NiuX.Dapper.Visitors;

namespace NiuX.Dapper.Querables;

public class DbOperationable<T>
{
    public IDataProviderFactory DataProviderFactory { get; set; }=NiuX.Dapper.DataProviderFactory.Instance;

    public IDbQuerableContextModel<T> ContextModel { get; set; } = new DbQuerableContextModel<T>();

    public DbOperationable<T> Where(Expression<Func<T, bool>> expression)
    {
        ContextModel.WhereExpression = expression;
        return this;
    }


    protected List<TResult> Query<TResult>(string sql)
    {
        using var conn = DataProviderFactory.GetConnection(ContextModel.ConnectionString);
        return conn.Query<TResult>(sql).ToList();
    }


    protected List<TResult> Query<TResult>(string sql, object param)
    {
        using var conn = DataProviderFactory.GetConnection(ContextModel.ConnectionString);
        return conn.Query<TResult>(sql, param).ToList();
    }


    public int Update(T entity)
    {
        return Update(entity, false);
    }

    public int Update(T entity, bool isIgnoreWhere)
    {
        if (!isIgnoreWhere && ContextModel.WhereExpression == null)
        {
            throw new Exception("更新时不要忘记填写 WHERE 子句");
        }

        var whereSql = "";

        if (ContextModel.WhereExpression != null)
        {
            whereSql = "WHERE " + new WhereVisitor().ResolveExpression(ContextModel.WhereExpression);
        }

        var sql = $@"UPDATE {ContextModel.TableName} SET {string.Join(",", entity.GetType().GetProperties().Select(x => $"{((MemberInfo)x).GetCustomAttribute<ColumnAttribute>()?.Name ?? x.Name} = @{x.Name}"))} 
                    {whereSql}";

        return Execute(sql, entity);
    }

    private int Execute(string sql)
    {
        using var conn = DataProviderFactory.GetConnection(ContextModel.ConnectionString);
        return conn.Execute(sql);
    }

    private int Execute(string sql, object param)
    {
        using var conn = DataProviderFactory.GetConnection(ContextModel.ConnectionString);
        return conn.Execute(sql, param);
    }




    public int Insert(T entity)
    {
        var sql = $"INSERT INTO {ContextModel.TableName} ({string.Join(",", ContextModel.EntityType.GetProperties().Select(x => $"{x.GetCustomAttribute<ColumnAttribute>()?.Name ?? x.Name}"))}) VALUES ({string.Join(",", entity.GetType().GetProperties().Select(x => $"@{x.Name}"))})";
        return Execute(sql, entity);
    }

    public int Insert(List<T> entities)
    {
        var sql = $"INSERT INTO {ContextModel.TableName} ({string.Join(",", ContextModel.EntityType.GetProperties().Select(x => $"{x.GetCustomAttribute<ColumnAttribute>()?.Name ?? x.Name}"))}) VALUES ({string.Join(",", ContextModel.EntityType.GetProperties().Select(x => $"@{x.Name}"))})";
        return Execute(sql, entities);
    }

    public int Delete()
    {
        return Execute($@"DELETE TABLE {ContextModel.TableName}");

    }

    public int DeleteAll()
    {
        return Execute($@"DELETE TABLE {ContextModel.TableName}");

    }

    /// <summary>
    /// Truncates this instance.
    /// </summary>
    /// <returns></returns>
    public int Truncate()
    {
        var sql = $@"TRUNCATE TABLE {ContextModel.TableName}";
        return Execute(sql);

    }

    /// <summary>
    /// Drops this instance.
    /// </summary>
    /// <returns></returns>
    public int Drop()
    {
        var sql = $@"DROP TABLE {ContextModel.TableName}";
        return Execute(sql);
    }
}